#include <functional>
#include <unordered_map>
#include <iostream>
#include <ns3/callback.h>
#include <ns3/packet.h>
#include <ns3/ptr.h>


#include <ns3/core-module.h>
#include <ns3/network-module.h>
#include <ns3/internet-module.h>
#include <ns3/applications-module.h>
#include <unordered_map>
#include <vector>
#include <ns3/wifi-module.h>
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/wifi-module.h"
#include "ns3/internet-module.h"
#include "ns3/wifi-mac-header.h"
#include "ns3/wifi-tx-vector.h"

using namespace std;
// using namespace ns3;

namespace pysched {
    std::function<void()> g_cb;
    void set(std::function<void()> f) { g_cb = std::move(f); }
    void tick() { if (g_cb) g_cb(); }

    void clear_all() {
        g_cb = std::function<void()>();
    }
}

namespace duavsim {
    using namespace ns3;
    // 单例句柄：所有地方都通过 Log() 拿同一份对象
    inline std::ofstream &Log() {
        static std::ofstream s;
        return s;
    }

    void InitLogging(const std::string &filename) {
        auto &f = Log();
        // 可选：确保目录存在
        std::filesystem::path p(filename);
        if (p.has_parent_path()) {
            std::error_code ec;
            std::filesystem::create_directories(p.parent_path(), ec);
            if (ec) std::cerr << "mkdirs failed: " << ec.message() << std::endl;
        }

        f.open(filename, std::ios::out | std::ios::app);
        if (!f.is_open()) {
            std::cerr << "Failed to open log file: " << filename << std::endl;
            return;
        }
        f << std::unitbuf;
        f << "Logging initialized successfully." << std::endl;
    }

    void CloseLogging() {
        auto &f = Log();
        if (f.is_open()) {
            f.flush();
            f.close();
        }
    }

    struct Stat {
        uint64_t valid = 0, error = 0, unknown = 0;
        uint64_t count_rx_times = 0; // 收到的次数
        uint64_t count_tx_times = 0; // 发送的次数
        uint64_t count_rx_byte = 0; // 收到的数据字节数
        uint64_t count_tx_byte = 0; // 发送的数据字节数
    };

    static bool HasLocalAddr(ns3::Ptr<ns3::Ipv4> ipv4, ns3::Ipv4Address ip) {
        using namespace ns3;
        if (!ipv4) return false;
        for (uint32_t i = 0; i < ipv4->GetNInterfaces(); ++i) {
            for (uint32_t j = 0; j < ipv4->GetNAddresses(i); ++j) {
                if (ipv4->GetAddress(i, j).GetLocal() == ip) return true;
            }
        }
        return false;
    }

    // 从 addTraceDirect 传入的 nodeType 中取 AUAV 编号（形如 "AUAV:3"）
    static uint32_t ParseAuavId(const std::string &nodeType, uint32_t fallback) {
        if (nodeType.rfind("AUAV", 0) == 0) {
            auto pos = nodeType.find(':');
            if (pos != std::string::npos) {
                try { return (uint32_t) std::stoul(nodeType.substr(pos + 1)); } catch (...) {
                }
            }
        }
        return fallback; // 兜底用 ns-3 的 nodeId（虽然通常不同，但不影响统计可用性）
    }

    using AUAVStats = std::unordered_map<uint32_t, std::unordered_map<uint32_t, Stat> >;
    using GCCStats = std::unordered_map<uint32_t, std::unordered_map<uint32_t, Stat> >;
    using DUAVStats = std::unordered_map<uint32_t, Stat>;

    static constexpr uint32_t K_AUAV_AGG_KEY = 0xFFFFFFFFu - 1; // 已有：聚合发送专用键
    static constexpr uint32_t K_AUAV_FWD_KEY = 0xFFFFFFFFu - 2; // 新增：转发统计
    static constexpr uint32_t K_AUAV_FWD_DROP_KEY = 0xFFFFFFFFu - 3;
    static constexpr uint32_t kAggKey = K_AUAV_AGG_KEY;

    // static std::unordered_map<uint32_t, std::unordered_map<uint32_t, Stat>> g_auav_stats; // key = AUAV nodeId @ fixme 这的逻辑明显是错的，不能按照AUAV整体来统计，肯定是要按照 对应的DUAV来统计，但是标记还是对应的DUAV的。
    // static std::unordered_map<uint32_t, std::unordered_map<uint32_t, Stat>> g_gcc_stats; // key = GCC nodeId @ fixme 同上
    // static std::unordered_map<uint32_t, Stat> g_duav_stats; // key = DUAV nodeId
    static AUAVStats &g_auav_stats() {
        static auto *p = new AUAVStats;
        return *p;
    }

    static GCCStats &g_gcc_stats() {
        static auto *p = new GCCStats;
        return *p;
    }

    static DUAVStats &g_duav_stats() {
        static auto *p = new DUAVStats;
        return *p;
    }

    static std::string GetAddrIP(const Address &from);

    // 判定一条“记录”（报文或报文片段）是否全 0x01 / 全 0x02
    static bool is_all(const uint8_t *data, uint32_t len, uint8_t v) {
        for (uint32_t i = 0; i < len; i++) if (data[i] != v) return false;
        return true;
    }

    static int classify_record(const uint8_t *data, uint32_t len) {
        if (is_all(data, len, 0x01)) return 1; // valid
        if (is_all(data, len, 0x02)) return 2; // error
        return 0; // unknown
    }

    static void set_global_seed(uint32_t seed) {
        ns3::RngSeedManager::SetSeed(seed);
    }

    // =============== DUAV 端：定时发送一条“记录” ===============
    class DuavTxApp : public Application {
    public:
        DuavTxApp() = default;

        void Configure(Ipv4Address dst, uint16_t dport, double interval, uint32_t recSize, double errProb,
                       uint32_t duavId) {
            m_dst = InetSocketAddress(dst, dport);
            m_interval = Seconds(interval);
            m_recSize = recSize;
            m_errProb = errProb;
            m_duav_id = duavId;
            m_rng->SetStream(100 + duavId);
        }

    private:
        Ptr<Socket> m_sock;
        Address m_dst;
        EventId m_evt;
        Time m_interval;
        uint32_t m_recSize = 256;
        double m_errProb = 0.1; // 10% 错误包
        uint32_t m_duav_id = 0;

        Ptr<UniformRandomVariable> m_rng = CreateObject<UniformRandomVariable>();

        void StartApplication() override {
            m_sock = Socket::CreateSocket(GetNode(), UdpSocketFactory::GetTypeId());
            m_sock->Connect(m_dst);
            ScheduleNext();
        }

        void StopApplication() override {
            if (m_evt.IsPending()) {
                Simulator::Cancel(m_evt);
            }
            if (m_sock) {
                m_sock->Close();
                m_sock = nullptr;
            }
        }

        void ScheduleNext() {
            m_evt = Simulator::Schedule(m_interval, &DuavTxApp::SendOne, this);
        }

        void SendOne() {
            bool is_err = false;
            std::vector<uint8_t> buf(m_recSize, 0x01);
            if (m_rng->GetValue() < m_errProb) {
                std::fill(buf.begin(), buf.end(), 0x02);
                is_err = true;
            }
            Ptr<Packet> p = Create<Packet>(buf.data(), buf.size());
            m_sock->Send(p);
            // 要自己统计自己的。
            g_duav_stats()[m_duav_id].valid += is_err ? 0 : 1;
            g_duav_stats()[m_duav_id].error += is_err ? 1 : 0;
            g_duav_stats()[m_duav_id].count_tx_times += 1;
            g_duav_stats()[m_duav_id].count_tx_byte += buf.size();
            ScheduleNext();
        }
    };

    // =============== AUAV 端：接收多个 DUAV 记录，聚合后再发给 GCC ===============
    class AuavAggApp : public Application {
    public:
        AuavAggApp() = default;

        void Configure(uint16_t listenPort, Ipv4Address gcc, uint16_t gccPort,
                       double flushInterval, uint32_t recSize, uint32_t maxBatch, uint32_t auavId, string auavip) {
            m_listenPort = listenPort;
            m_gccAddr = InetSocketAddress(gcc, gccPort);
            m_flushInterval = Seconds(flushInterval);
            m_recSize = recSize;
            m_maxBatch = maxBatch;
            m_auav_id = auavId;
            m_ip = std::move(auavip);
        }

        void SetUp(std::string ip, uint32_t duav_id) {
            m_ip2id[ip] = duav_id;
        }

    private:
        uint16_t m_listenPort = 9000;
        Address m_gccAddr;
        Ptr<Socket> m_rx, m_tx;
        Time m_flushInterval;
        EventId m_flushEvt;
        uint32_t m_recSize = 256;
        uint32_t m_maxBatch = 32;
        string m_ip;
        uint32_t m_auav_id = 0;
        std::vector<uint8_t> m_batch; // 简单串接：N*recSize
        std::unordered_map<std::string, uint32_t> m_ip2id;

        void StartApplication() override {
            auto &log_file = Log();
            auto node = GetNode();
            if (node == nullptr) {
                log_file << "Error: Node is null!" << std::endl;
                return;
            }
            auto ipv4 = node->GetObject<Ipv4>();
            if (ipv4 == nullptr) {
                log_file << "Error: No IPv4 stack found on node!" << std::endl;
                return;
            }
            auto num_dev = node->GetNDevices();
            for (uint32_t i = 0; i < num_dev; i++) {
                auto dev = node->GetDevice(i);
                if (dev == nullptr) {
                    log_file << "Error: Device " << i << " is null!" << std::endl;
                    continue;
                }

                // 获取设备对应的接口索引
                int32_t interfaceIndex = ipv4->GetInterfaceForDevice(dev);
                if (interfaceIndex == -1) {
                    log_file << "Error: No interface found for device " << i << std::endl;
                    continue;
                }

                // 获取接口的 IP 地址数量
                uint32_t numAddresses = ipv4->GetNAddresses(interfaceIndex);
                if (numAddresses == 0) {
                    log_file << "Error: No IP address assigned to interface " << interfaceIndex << std::endl;
                    continue;
                }
                for (uint32_t j = 0; j < numAddresses; j++) {
                    // 获取第一个 IP 地址（通常只有一个）
                    Ipv4InterfaceAddress address = ipv4->GetAddress(interfaceIndex, j);
                    Ipv4Address ipv4Addr = address.GetLocal();
                    log_file << "AUAV " << m_auav_id << " working on IP " << ipv4Addr << " with network device " << i <<
                            " and m_ip " << m_ip << std::endl;
                }
            }
            m_rx = Socket::CreateSocket(node, UdpSocketFactory::GetTypeId());
            InetSocketAddress local = InetSocketAddress(m_ip.c_str(), m_listenPort);
            m_rx->Bind(local);
            m_rx->BindToNetDevice(node->GetDevice(1));
            m_rx->SetRecvCallback(MakeCallback(&AuavAggApp::HandleRead, this));

            m_tx = Socket::CreateSocket(node, UdpSocketFactory::GetTypeId());
            Ipv4InterfaceAddress address = ipv4->GetAddress(2, 0); // 这里用了硬编码 能跑 (网卡的编号， 网卡上IP的编号）
            Ipv4Address ipv4Addr = address.GetLocal();
            log_file << "AUAV " << m_auav_id << " m_tx working on IP " << ipv4Addr << std::endl;
            InetSocketAddress local2 = InetSocketAddress(ipv4Addr, 0);

            if (m_tx->Bind(local2) < 0) {
                log_file << "Error: TX Bind failed errno=" << m_tx->GetErrno() << "\n";
                return;
            }
            m_tx->BindToNetDevice(node->GetDevice(2));
            //m_tx->Connect(m_gccAddr);
            if (m_tx->Connect(m_gccAddr) < 0) {
                log_file << "Error: TX Connect failed errno=" << m_tx->GetErrno() << "\n";
                return;
            }
            Address loc, peer;
            m_tx->GetSockName(loc);
            m_tx->GetPeerName(peer);
            auto L = InetSocketAddress::ConvertFrom(loc);
            auto P = InetSocketAddress::ConvertFrom(peer);
            log_file << "[TX bind-check] AUAV " << m_auav_id
                    << " local " << L.GetIpv4() << ":" << L.GetPort()
                    << " -> peer " << P.GetIpv4() << ":" << P.GetPort()
                    << " on dev2\n";

            ScheduleFlush();
        }

        uint32_t GetIdByIp(const std::string &ip) {
            // return m_ip2id[ip];
            auto it = m_ip2id.find(ip);
            if (it != m_ip2id.end()) {
                return it->second;
            }
            // 处理不存在的情况
            return 0xFFFFFFFF; // 或者返回特定错误码，如 -1
        }

        void StopApplication() override {
            if (m_flushEvt.IsPending()) {
                Simulator::Cancel(m_flushEvt);
            }
            if (!m_batch.empty()) {
                Flush();
            } // 最后再冲一波
            if (m_rx) {
                m_rx->Close();
                m_rx = nullptr;
            }
            if (m_tx) {
                m_tx->Close();
                m_tx = nullptr;
            }
        }

        void HandleRead(Ptr<Socket> s) {
            Address from;
            Ptr<Packet> p;
            while ((p = s->RecvFrom(from))) {
                auto &log_file = Log();
                std::string ip = GetAddrIP(from);
                uint32_t duav_id = GetIdByIp(ip);
                if (duav_id == 0xFFFFFFFF) {
                    log_file << "AUAV " << m_auav_id << ": " << "DUAV IP " << ip << ": " << "unknown record" <<
                            std::endl;
                    continue;
                }
                g_auav_stats()[m_auav_id][duav_id].count_rx_times += 1;
                uint32_t sz = p->GetSize();
                g_auav_stats()[m_auav_id][duav_id].count_rx_byte += sz;

                std::vector<uint8_t> tmp(sz);
                p->CopyData(tmp.data(), sz);

                // 只接受完整记录；不满一条直接丢弃（可按需放宽）
                if (sz != m_recSize) continue;

                // 统计（本 AUAV 节点）
                // uint32_t nid = GetNode()->GetId();
                int c = classify_record(tmp.data(), sz);

                if (c == 1) duavsim::g_auav_stats()[m_auav_id][duav_id].valid++;
                else if (c == 2) duavsim::g_auav_stats()[m_auav_id][duav_id].error++;
                else duavsim::g_auav_stats()[m_auav_id][duav_id].unknown++;

                // 入批
                m_batch.insert(m_batch.end(), tmp.begin(), tmp.end());
                if (m_batch.size() / m_recSize >= m_maxBatch) {
                    Flush();
                }
            }
        }

        void ScheduleFlush() {
            m_flushEvt = Simulator::Schedule(m_flushInterval, &AuavAggApp::FlushTick, this);
        }

        void FlushTick() {
            if (!m_batch.empty()) Flush();
            ScheduleFlush();
        }

        void Flush() {
            auto &log_file = Log();
            if (m_batch.empty()) return;
            g_auav_stats()[m_auav_id][kAggKey].count_tx_byte += m_batch.size(); // 用 0xFFFFFFFF - 1 表示聚合包
            g_auav_stats()[m_auav_id][kAggKey].count_tx_times += 1;
            Ptr<Packet> agg = Create<Packet>(m_batch.data(), m_batch.size());
            auto count = m_tx->Send(agg);
            if (count < 0) {
                log_file << "Error: Sent to GCC failed errno=" << m_tx->GetErrno() << "\n";
            }
            //log_file<< "AUAV" << m_auav_id << ": " << "Flush " << m_batch.size() << " bytes" << std::endl;
            m_batch.clear();
        }
    };

    void ClearGlobals() {
        g_duav_stats().clear();
        g_auav_stats().clear();
        g_gcc_stats().clear();
    }

    // 把清理动作挂到 ns-3 的 Destroy 阶段（Run 结束、Destroy 之前触发）
    void ScheduleCleanup() {
        ns3::Simulator::ScheduleDestroy(ns3::MakeEvent(&pysched::clear_all));
        ns3::Simulator::ScheduleDestroy(ns3::MakeEvent(&duavsim::ClearGlobals));
    }

    // =============== GCC 端：接收聚合包，按记录切片统计 ===============
    class GccCollectorApp : public Application {
    public:
        GccCollectorApp() = default;

        void Configure(uint16_t listenPort, uint32_t recSize, uint32_t gccId) {
            m_listenPort = listenPort;
            m_recSize = recSize;
            m_gcc_id = gccId;
        }

        void SetUp(std::string ip, uint32_t auav_id) {
            auto &log_file = Log();
            m_ip2id[ip] = auav_id;
            log_file << "GCC " << m_gcc_id << " adding " << "AUAV IP " << ip << ", id" << auav_id << std::endl;
        }

    private:
        uint16_t m_listenPort = 9100;
        uint32_t m_recSize = 256;
        Ptr<Socket> m_rx;
        uint32_t m_gcc_id = 0;
        std::unordered_map<std::string, uint32_t> m_ip2id;

        uint32_t GetIdByIp(const std::string &ip) {
            // return m_ip2id[ip];

            auto it = m_ip2id.find(ip);
            if (it != m_ip2id.end()) {
                //log_file << "GCC query a id by ip:" <<ip <<" , and the result is "<< it->second << std::endl;
                return it->second;
            }
            //log_file << "GCC query a id by ip:" <<ip <<" , and the result is "<< 0xFFFFFFFF << std::endl;
            // 处理不存在的情况
            return 0xFFFFFFFF; // 或者返回特定错误码，如 -1
        }

        void StartApplication() override {
            m_rx = Socket::CreateSocket(GetNode(), UdpSocketFactory::GetTypeId());
            InetSocketAddress local = InetSocketAddress(Ipv4Address::GetAny(), m_listenPort);
            m_rx->Bind(local);
            m_rx->SetRecvCallback(MakeCallback(&GccCollectorApp::HandleRead, this));
        }

        void StopApplication() override {
            if (m_rx) {
                m_rx->Close();
                m_rx = nullptr;
            }
        }

        void HandleRead(Ptr<Socket> s) {
            auto &log_file = Log();
            Address from;
            Ptr<Packet> p;
            // uint32_t nid = GetNode()->GetId();
            while ((p = s->RecvFrom(from))) {
                std::string ip = GetAddrIP(from);
                uint32_t auav_id = GetIdByIp(ip);
                if (auav_id == 0xFFFFFFFF) {
                    log_file << "GCC " << m_gcc_id << ": " << "AUAV IP " << ip << ": " << "unknown record" << std::endl;
                    continue;
                }
                // log_file << "GCC " << m_gcc_id << ": " << "recv form AUAV" << auav_id << std::endl;
                g_gcc_stats()[m_gcc_id][auav_id].count_rx_times += 1;
                uint32_t sz = p->GetSize();
                g_gcc_stats()[m_gcc_id][auav_id].count_rx_byte += sz;
                if (sz == 0) continue;
                std::vector<uint8_t> buf(sz);
                p->CopyData(buf.data(), sz);

                // 按固定记录大小切分
                uint32_t nrec = m_recSize ? (sz / m_recSize) : 0;
                for (uint32_t i = 0; i < nrec; i++) {
                    uint8_t *rec = buf.data() + i * m_recSize;
                    int c = classify_record(rec, m_recSize);

                    if (c == 1) {
                        duavsim::g_gcc_stats()[m_gcc_id][auav_id].valid++;
                    } else if (c == 2) {
                        duavsim::g_gcc_stats()[m_gcc_id][auav_id].error++;
                    } else {
                        duavsim::g_gcc_stats()[m_gcc_id][auav_id].unknown++;
                    }
                }
            }
        }
    };

    // =============== 便捷安装器 + 统计导出 ===============
    static ns3::Ptr<ns3::Application> InstallDuavApp(ns3::Ptr<ns3::Node> n, ns3::Ipv4Address dst, uint16_t dport,
                                                     double interval, uint32_t recSize, double errProb,
                                                     uint32_t duav_id) {
        Ptr<DuavTxApp> app = CreateObject<DuavTxApp>();
        app->Configure(dst, dport, interval, recSize, errProb, duav_id);
        n->AddApplication(app);
        return app;
    }

    static std::string GetAddrIP(const Address &from) {
        if (InetSocketAddress::IsMatchingType(from)) {
            // IPv4 地址
            InetSocketAddress addr = InetSocketAddress::ConvertFrom(from);
            Ipv4Address ip = addr.GetIpv4();
            // uint16_t port = addr.GetPort();
            std::ostringstream oss;
            // oss << addr;
            ip.Print(oss);
            std::string ip_str = oss.str();
            // log_file << "Received from IPv4: " << ip_str << ":" << port << std::endl;
            return ip_str;
        }
        if (Inet6SocketAddress::IsMatchingType(from)) {
            // IPv6 地址
            Inet6SocketAddress addr = Inet6SocketAddress::ConvertFrom(from);
            Ipv6Address ip = addr.GetIpv6();
            // uint16_t port = addr.GetPort();
            std::ostringstream oss;
            ip.Print(oss);
            std::string ip_str = oss.str();

            // log_file << "Received from IPv6: " << ip << ":" << port << std::endl;
            return ip_str;
        }
        // 未知地址类型
        // log_file << "Received from unknown address type" << std::endl;
        return "";
    }

    static ns3::Ptr<ns3::Application> InstallAuavAggApp(ns3::Ptr<ns3::Node> n, uint16_t listenPort,
                                                        ns3::Ipv4Address gcc, uint16_t gccPort,
                                                        double flushInterval, uint32_t recSize, uint32_t maxBatch,
                                                        uint32_t auav_id, string auav_ip) {
        auto &log_file = Log();
        Ptr<AuavAggApp> app = CreateObject<AuavAggApp>();
        app->Configure(listenPort, gcc, gccPort, flushInterval, recSize, maxBatch, auav_id, auav_ip);
        log_file << "AUAV " << auav_id << " with ip " << auav_ip << " bind port " << listenPort << std::endl;
        n->AddApplication(app);
        return app;
    }

    static ns3::Ptr<ns3::Application> InstallGccCollector(ns3::Ptr<ns3::Node> n, uint16_t listenPort, uint32_t recSize,
                                                          uint32_t gcc_id) {
        Ptr<GccCollectorApp> app = CreateObject<GccCollectorApp>();
        app->Configure(listenPort, recSize, gcc_id);
        n->AddApplication(app);
        return app;
    }

    // 导出统计（Python 可调用）
    static unordered_map<uint32_t, duavsim::Stat> GetAuavStat(uint32_t nodeId) { return g_auav_stats()[nodeId]; }
    static unordered_map<uint32_t, duavsim::Stat> GetGccStat(uint32_t nodeId) { return g_gcc_stats()[nodeId]; }
    static duavsim::Stat GetDuavStat(uint32_t nodeId) { return g_duav_stats()[nodeId]; }


#define dumpstats_output_file "/mnt/f/Users/Administrator/ldst_2/pythonProject/report/report-{placeholder}.csv"
    // 打印所有统计（可在仿真尾调用）
    // ——替换原 DumpStats() 函数体为下面这个实现
    static void DumpStats() {
        auto &log_file = Log();
        using namespace std;
        // -------------------------
        // 1) 如需 CSV：准备文件与写行工具
        // -------------------------
#ifdef dumpstats_output_file
        const char *__csv_path = dumpstats_output_file; // 例如 -Ddumpstats_output_file=\"stats.csv\"
        bool __csv_enabled = true;
        bool __need_header = false; {
            std::ifstream __check(__csv_path, std::ios::in);
            __need_header = !(__check.good() && __check.peek() != std::ifstream::traits_type::eof());
        }
        std::ofstream __csv_ofs(__csv_path, std::ios::app);
        if (!__csv_ofs) {
            log_file << "[DumpStats] Failed to open CSV file: " << __csv_path << "\n";
            std::cout << "[DumpStats] Failed to open CSV file: " << __csv_path << "\n";
            __csv_enabled = false;
        }
        auto __csv_write_header = [&]() {
            if (!__csv_enabled) return;
            __csv_ofs
                    << "sim_time_s,section,node_id,peer_id,valid,error,unknown,rx_times,rx_bytes,tx_times,tx_bytes\n";
        };
        auto __csv_emit = [&](const std::string &section, uint32_t node_id, uint32_t peer_id, const duavsim::Stat &s) {
            if (!__csv_enabled) return;
            __csv_ofs << std::fixed << std::setprecision(3) << ns3::Simulator::Now().GetSeconds() << ','
                    << section << ','
                    << node_id << ','
                    << peer_id << ','
                    << s.valid << ','
                    << s.error << ','
                    << s.unknown << ','
                    << s.count_rx_times << ','
                    << s.count_rx_byte << ','
                    << s.count_tx_times << ','
                    << s.count_tx_byte << '\n';
        };
        if (__need_header) __csv_write_header();
#endif

        // -------------------------
        // 2) 原有的控制台打印逻辑（保持不变）
        // -------------------------
        uint64_t duav_v = 0, duav_e = 0, duav_u = 0;
        uint64_t auav_v = 0, auav_e = 0, gcc_v = 0, gcc_e = 0;
        uint64_t auav_u = 0, gcc_u = 0;
        uint64_t duav_tx_c = 0, duav_tx_b = 0;
        uint64_t auav_tx_c = 0, auav_tx_b = 0;
        uint64_t auav_rx_t = 0, auav_rx_b = 0;
        uint64_t gcc_rx_t = 0, gcc_rx_b = 0;
        const uint32_t kFwdKey      = K_AUAV_FWD_KEY;
        const uint32_t kFwdDropKey  = K_AUAV_FWD_DROP_KEY;

        // 输出 DUAV 统计
        log_file << "[DUAV stats]\n";
        std::cout << "[DUAV stats]\n";
        auto _g_duav_stats = g_duav_stats();
        for (const auto &kv: _g_duav_stats) {
            uint32_t duav_id = kv.first;
            const auto &stat = kv.second;
            duav_v += stat.valid;
            duav_e += stat.error;
            duav_u += stat.unknown;
            duav_tx_c += stat.count_tx_times;
            duav_tx_b += stat.count_tx_byte;
            log_file << "  DUAV Node " << duav_id
                    << "  valid=" << stat.valid
                    << "  error=" << stat.error
                    << "  unknown=" << stat.unknown
                    << "  tx count=" << stat.count_tx_times
                    << "  tx bytes=" << stat.count_tx_byte
                    << "\n";
            std::cout << "  DUAV Node " << duav_id
                    << "  valid=" << stat.valid
                    << "  error=" << stat.error
                    << "  unknown=" << stat.unknown
                    << "  tx count=" << stat.count_tx_times
                    << "  tx bytes=" << stat.count_tx_byte
                    << "\n";
#ifdef dumpstats_output_file
            __csv_emit("DUAV", duav_id, 0u, stat);
#endif
        }
        log_file << "  [DUAV total] valid=" << duav_v
                << "  error=" << duav_e
                << "  unknown=" << duav_u
                << "  duav_tx_c=" << duav_tx_c
                << "  duav_tx_b=" << duav_tx_b
                << "\n";
        std::cout << "  [DUAV total] valid=" << duav_v
                << "  error=" << duav_e
                << "  unknown=" << duav_u
                << "  duav_tx_c=" << duav_tx_c
                << "  duav_tx_b=" << duav_tx_b
                << "\n";
#ifdef dumpstats_output_file
        {
            duavsim::Stat tot{};
            tot.valid = duav_v;
            tot.error = duav_e;
            tot.unknown = duav_u;
            tot.count_tx_times = duav_tx_c;
            tot.count_tx_byte = duav_tx_b;
            __csv_emit("DUAV_TOTAL", 0u, 0u, tot);
        }
#endif

        // 输出 AUAV 统计
        auto _g_auav_stats = g_auav_stats();
        for (const auto &auav_kv: _g_auav_stats) {
            uint32_t auav_id = auav_kv.first;
            const auto &duav_stats = auav_kv.second;

            log_file << "  AUAV Node " << auav_id << ":\n";
            std::cout << "  AUAV Node " << auav_id << ":\n";
            uint64_t auav_v_total = 0, auav_e_total = 0, auav_u_total = 0;
            uint64_t auav_rx_t_total = 0, auav_rx_b_total = 0;

            // 常量：聚合包的“哨兵”键


            for (const auto &duav_kv: duav_stats) {
                uint32_t duav_id = duav_kv.first;
                const auto &stat = duav_kv.second;

                // 跳过“聚合发送”条目（该条目只统计 TX，单独显示）
                if (duav_id != kAggKey && duav_id != kFwdKey && duav_id != kFwdDropKey) {
                    auav_v_total += stat.valid;
                    auav_e_total += stat.error;
                    auav_u_total += stat.unknown;
                    auav_rx_t_total += stat.count_rx_times;
                    auav_rx_b_total += stat.count_rx_byte;
                    log_file << "    DUAV " << duav_id
                            << "  valid=" << stat.valid
                            << "  error=" << stat.error
                            << "  unknown=" << stat.unknown
                            << "  rx times=" << stat.count_rx_times
                            << "  rx byte=" << stat.count_rx_byte
                            << "\n";
                    std::cout << "    DUAV " << duav_id
                            << "  valid=" << stat.valid
                            << "  error=" << stat.error
                            << "  unknown=" << stat.unknown
                            << "  rx times=" << stat.count_rx_times
                            << "  rx byte=" << stat.count_rx_byte
                            << "\n";
#ifdef dumpstats_output_file
                    __csv_emit("AUAV", auav_id, duav_id, stat);
#endif
                }
            }
            // @fixme need reviewer again
            if (duav_stats.find(kFwdKey) != duav_stats.end()) {
                const auto& fwd = duav_stats.at(kFwdKey);
                log_file << "    [AUAV_FWD]  tx_times=" << fwd.count_tx_times
                         << " tx_bytes=" << fwd.count_tx_byte << "\n";
#ifdef dumpstats_output_file
                __csv_emit("AUAV_FWD", auav_id, 0u, fwd);
#endif
            }
            if (duav_stats.find(kFwdDropKey) != duav_stats.end()) {
                const auto& fdrop = duav_stats.at(kFwdDropKey);
                log_file << "    [AUAV_FWD_DROP]  drops=" << fdrop.error
                         << " drop_bytes=" << fdrop.count_tx_byte << "\n";
#ifdef dumpstats_output_file
                __csv_emit("AUAV_FWD_DROP", auav_id, 0u, fdrop);
#endif
            }
            // AUAV 聚合发送条目（kAggKey）
            duavsim::Stat aggStat{};
            auto itAgg = duav_stats.find(kAggKey);
            if (itAgg != duav_stats.end()) {
                aggStat = itAgg->second;
                // 控制台里 TX 聚合是汇总在 [Total] 行里显示，
                // 如需单独在 CSV 也保留一行，使用 section=AUAV_AGG：
#ifdef dumpstats_output_file
                __csv_emit("AUAV_AGG", auav_id, kAggKey, aggStat);
#endif
            }
            log_file << "    [Total] valid=" << auav_v_total
                    << "  error=" << auav_e_total
                    << "  unknown=" << auav_u_total
                    << "  count_tx_times=" << aggStat.count_tx_times
                    << "  count_tx_byte=" << aggStat.count_tx_byte
                    // << "  count_rx_times=" << aggStat.count_rx_times
                    // << "  count_rx_byte=" << aggStat.count_rx_byte
                    << "\n";
            std::cout << "    [Total] valid=" << auav_v_total
                    << "  error=" << auav_e_total
                    << "  unknown=" << auav_u_total
                    << "  count_tx_times=" << aggStat.count_tx_times
                    << "  count_tx_byte=" << aggStat.count_tx_byte
                    // << "  count_rx_times=" << aggStat.count_rx_times
                    // << "  count_rx_byte=" << aggStat.count_rx_byte
                    << "\n";
            // 全局 AUAV 汇总
            auav_v += auav_v_total;
            auav_e += auav_e_total;
            auav_u += auav_u_total;
            auav_rx_t += auav_rx_t_total;
            auav_rx_b += auav_rx_b_total;
            auav_tx_c += aggStat.count_tx_times;
            auav_tx_b += aggStat.count_tx_byte;
        }

#ifdef dumpstats_output_file
        {
            duavsim::Stat tot{};
            tot.valid = auav_v;
            tot.error = auav_e;
            tot.unknown = auav_u;
            tot.count_rx_times = auav_rx_t;
            tot.count_rx_byte = auav_rx_b;
            tot.count_tx_times = auav_tx_c;
            tot.count_tx_byte = auav_tx_b;
            __csv_emit("AUAV_TOTAL", 999, 0u, tot);
        }
#endif
        log_file << "  [AUAV total] valid=" << auav_v
                << "  error=" << auav_e
                << "  unknown=" << auav_u
                << "  auav_rx_t=" << auav_rx_t
                << "  auav_rx_b=" << auav_rx_b
                << "\n";
        std::cout << "  [AUAV total] valid=" << auav_v
                << "  error=" << auav_e
                << "  unknown=" << auav_u
                << "  auav_rx_t=" << auav_rx_t
                << "  auav_rx_b=" << auav_rx_b
                << "\n";

        // 输出 GCC 统计
        log_file << "[GCC stats]\n";
        std::cout << "[GCC stats]\n";
        auto _g_gcc_stats = g_gcc_stats();
        for (const auto &gcc_kv: _g_gcc_stats) {
            uint32_t gcc_id = gcc_kv.first;
            const auto &auav_stats = gcc_kv.second;

            log_file << "  GCC Node " << gcc_id << ":\n";
            std::cout << "  GCC Node " << gcc_id << ":\n";
            uint64_t gcc_v_total = 0, gcc_e_total = 0, gcc_u_total = 0;
            uint64_t gcc_rx_t_total = 0, gcc_rx_b_total = 0;

            for (const auto &auav_kv: auav_stats) {
                uint32_t auav_id = auav_kv.first;
                const auto &stat = auav_kv.second;
                gcc_v_total += stat.valid;
                gcc_e_total += stat.error;
                gcc_u_total += stat.unknown;
                gcc_rx_t_total += stat.count_rx_times;
                gcc_rx_b_total += stat.count_rx_byte;
                log_file << "    AUAV " << auav_id
                        << "  valid=" << stat.valid
                        << "  error=" << stat.error
                        << "  unknown=" << stat.unknown
                        << "  rx_times=" << stat.count_rx_times
                        << "  rx_byte=" << stat.count_rx_byte
                        << "\n";
                std::cout << "    AUAV " << auav_id
                        << "  valid=" << stat.valid
                        << "  error=" << stat.error
                        << "  unknown=" << stat.unknown
                        << "  rx_times=" << stat.count_rx_times
                        << "  rx_byte=" << stat.count_rx_byte
                        << "\n";
#ifdef dumpstats_output_file
                __csv_emit("GCC", gcc_id, auav_id, stat);
#endif
            }
            log_file << "    [Total] valid=" << gcc_v_total
                    << "  error=" << gcc_e_total
                    << "  unknown=" << gcc_u_total
                    << "  count_rx_times=" << gcc_rx_t_total
                    << "  count_rx_byte=" << gcc_rx_b_total
                    << "\n";
            std::cout << "    [Total] valid=" << gcc_v_total
                    << "  error=" << gcc_e_total
                    << "  unknown=" << gcc_u_total
                    << "  count_rx_times=" << gcc_rx_t_total
                    << "  count_rx_byte=" << gcc_rx_b_total
                    << "\n";

            gcc_v += gcc_v_total;
            gcc_e += gcc_e_total;
            gcc_u += gcc_u_total;
            gcc_rx_t += gcc_rx_t_total;
            gcc_rx_b += gcc_rx_b_total;

#ifdef dumpstats_output_file
            {
                duavsim::Stat tot{};
                tot.valid = gcc_v_total;
                tot.error = gcc_e_total;
                tot.unknown = gcc_u_total;
                tot.count_rx_times = gcc_rx_t_total;
                tot.count_rx_byte = gcc_rx_b_total;
                __csv_emit("GCC_TOTAL", gcc_id, 0u, tot);
            }
#endif
        }

        log_file << "  [GCC total] valid=" << gcc_v
                << "  error=" << gcc_e
                << "  unknown=" << gcc_u
                << "  gcc_rx_t=" << gcc_rx_t
                << "  gcc_rx_b=" << gcc_rx_b
                << "\n";
        std::cout << "  [GCC total] valid=" << gcc_v
                << "  error=" << gcc_e
                << "  unknown=" << gcc_u
                << "  gcc_rx_t=" << gcc_rx_t
                << "  gcc_rx_b=" << gcc_rx_b
                << "\n";
    }
#undef dumpstats_output_file

    // 直接调我们的统计打印
    static void DumpStatsThunk() { DumpStats(); }
    // 相对时间（秒）后打印
    void ScheduleDumpIn(double sec) {
        ns3::Simulator::Schedule(ns3::Seconds(sec), ns3::MakeEvent(&DumpStatsThunk));
    }

    // 绝对时刻（秒）打印
    void ScheduleDumpAt(double abs_sec) {
        double delay = abs_sec - ns3::Simulator::Now().GetSeconds();
        if (delay < 0) delay = 0;
        ScheduleDumpIn(delay);
    }

    using TraceHelper = std::unordered_map<uint32_t, std::string>;

    static TraceHelper &getTraceHelper() {
        static auto p = new TraceHelper;
        return *p;
    }

    void TraceL3TxNoCtx(ns3::Ptr<const ns3::Packet> p, ns3::Ptr<ns3::Ipv4> ipv4, uint32_t iface) {
        auto &log_file = Log();
        Ptr<Node> node = ipv4->GetObject<Node>();
        uint32_t nodeId = node->GetId();

        // 从全局映射表获取节点类型
        std::string nodeType = "UNKNOWN";
        if (getTraceHelper().find(nodeId) != getTraceHelper().end()) {
            nodeType = getTraceHelper()[nodeId];
        }

        ns3::Ipv4Header ih;
        ns3::Ptr<ns3::Packet> p2 = p->Copy();
        p2->PeekHeader(ih); // 先窥 IPv4 头，判断协议
        if (ih.GetProtocol() != 17) return; // 17 = UDP
        p2->RemoveHeader(ih); // 移走 IPv4 头，包头现在是 UDP

        ns3::UdpHeader uh;
        p2->PeekHeader(uh); // 这时才能正确读到端口
        log_file << "[L3-TX] NodeID=" << nodeId << " Type=" << nodeType
                << " iface=" << iface
                << " " << ih.GetSource() << " -> " << ih.GetDestination()
                << " sport=" << uh.GetSourcePort() << " dport=" << uh.GetDestinationPort()
                << std::endl;
        // @fixme 这里的统计用的代号是有问题的。
        // === 新增：统计“转发”的 TX（排除 OLSR/698，只保留上行聚合端口 9100）===
        if (nodeType.rfind("AUAV", 0) == 0) {
            const uint16_t sport = uh.GetSourcePort(), dport = uh.GetDestinationPort();
            if (dport == 9100 && sport != 698 && dport != 698) {
                // 若数据包源 IP 不是本机任一地址，则这是“中转转发”，而非本 AUAV 的本地产生
                const bool isForward = !HasLocalAddr(ipv4, ih.GetSource());
                if (isForward) {
                    const uint32_t auav_id = ParseAuavId(nodeType, nodeId);
                    auto &stat = g_auav_stats()[auav_id][K_AUAV_FWD_KEY];
                    stat.count_tx_times += 1;
                    stat.count_tx_byte  += p->GetSize(); // 用 L3 包长即可，口径统一即可
                }
            }
        }
    }

    void TraceL3DropNoCtx(const ns3::Ipv4Header &ih, ns3::Ptr<const ns3::Packet> p,
                          ns3::Ipv4L3Protocol::DropReason reason, ns3::Ptr<ns3::Ipv4> ipv4, uint32_t iface) {
        Ptr<Node> node = ipv4->GetObject<Node>();
        uint32_t nodeId = node->GetId();
        auto &log_file = Log();
        // 从全局映射表获取节点类型
        std::string nodeType = "UNKNOWN";
        if (getTraceHelper().find(nodeId) != getTraceHelper().end()) {
            nodeType = getTraceHelper()[nodeId];
        }

        uint16_t dport = 0, sport = 0;
        if (ih.GetProtocol() == 17 && p && p->GetSize() >= 8) {
            // UDP header 至少 8 字节
            ns3::Ptr<ns3::Packet> p2 = p->Copy();
            ns3::UdpHeader uh;
            p2->PeekHeader(uh); // 某些 Drop 点 p 就是 L4 开头
            sport = uh.GetSourcePort();
            dport = uh.GetDestinationPort();
        }
        // Ptr<Node> node = ns3::NodeList::GetNode(nodeId);
        log_file << "[L3-DROP] NodeID=" << nodeId << " Type=" << nodeType
                << " iface=" << iface << " reason=" << reason
                << " " << ih.GetSource() << " -> " << ih.GetDestination()
                << " proto=" << (unsigned) ih.GetProtocol()
                << " sport=" << sport << " dport=" << dport
                << std::endl;
        // @fixme 这里的统计用的代号是有问题的。
        // === 新增：仅统计“转发”方向的 L3 丢弃（上行 9100，排除 OLSR/698）===
        if (nodeType.rfind("AUAV", 0) == 0 && ih.GetProtocol() == 17) {
            if (dport == 9100 && sport != 698 && dport != 698) {
                const bool isForward = !HasLocalAddr(ipv4, ih.GetSource());
                if (isForward) {
                    const uint32_t auav_id = ParseAuavId(nodeType, nodeId);
                    auto &dropStat = g_auav_stats()[auav_id][K_AUAV_FWD_DROP_KEY];
                    dropStat.error += 1;
                    if (p) dropStat.count_tx_byte += p->GetSize(); // 记录被丢弃的字节量（可选）
                }
            }
        }
    }

    void TraceL3RxNoCtx(ns3::Ptr<const ns3::Packet> p, ns3::Ptr<ns3::Ipv4> ipv4, uint32_t iface) {
        Ptr<Node> node = ipv4->GetObject<Node>();
        uint32_t nodeId = node->GetId();
        auto &log_file = Log();
        // 从全局映射表获取节点类型
        std::string nodeType = "UNKNOWN";
        if (getTraceHelper().find(nodeId) != getTraceHelper().end()) {
            nodeType = getTraceHelper()[nodeId];
        }

        ns3::Ipv4Header ih;

        auto q = p->Copy();

        q->PeekHeader(ih);
        if (ih.GetProtocol() == 17) {
            q->RemoveHeader(ih);
            ns3::UdpHeader uh;
            q->PeekHeader(uh);

            log_file << "[L3-RX] NodeID=" << nodeId << " Type=" << nodeType
                    << " iface=" << iface
                    << " " << ih.GetSource() << " -> " << ih.GetDestination()
                    << " sport=" << uh.GetSourcePort() << " dport=" << uh.GetDestinationPort()
                    << std::endl;
        }
    }

    void addTraceDirect(uint32_t nodeId, const std::string &nodeType) {
        using namespace ns3;
        // 存储节点类型到全局映射表
        getTraceHelper()[nodeId] = nodeType;
        auto &log_file = Log();
        Ptr<Node> node = NodeList::GetNode(nodeId);

        if (!node) {
            log_file << "addTraceDirect: Node " << nodeId << " not found\n";
            return;
        }

        Ptr<Ipv4L3Protocol> l3 = node->GetObject<Ipv4L3Protocol>();
        if (!l3) {
            log_file << "addTraceDirect: Node " << nodeId << " has no Ipv4L3Protocol\n";
            return;
        }

        l3->TraceConnectWithoutContext("Tx", MakeCallback(&TraceL3TxNoCtx));
        //if (nodeType == "GCC") {
        // 只在GCC 节点上监听 Rx
        l3->TraceConnectWithoutContext("Rx", MakeCallback(&TraceL3RxNoCtx)); // ★ 新增
        //}
        l3->TraceConnectWithoutContext("Drop", MakeCallback(&TraceL3DropNoCtx));
        log_file << "addTraceDirect: hooked L3 Tx/Drop on node " << nodeId << std::endl;
    }

    using namespace ns3;

    // 从 ctx 里取 Node/Dev 号（方便定位）
    static uint32_t CtxNode(const std::string &ctx) {
        auto p = ctx.find("NodeList/");
        p += 9;
        auto e = ctx.find('/', p);
        return std::stoul(ctx.substr(p, e - p));
    }

    static uint32_t CtxDev(const std::string &ctx) {
        auto p = ctx.find("DeviceList/");
        p += 11;
        auto e = ctx.find('/', p);
        return std::stoul(ctx.substr(p, e - p));
    }

    // 解析 802.11 + LLC/SNAP → IPv4 → UDP，方便在 L2 打出五元组
    static bool PeekIpv4Udp(Ptr<const Packet> pkt, Ipv4Header &ih, UdpHeader &uh, bool &isUdp) {
        using namespace ns3;
        Ptr<Packet> p = pkt->Copy();

        // 1) 先看 802.11 MAC 头
        WifiMacHeader wh;
        if (!p->PeekHeader(wh))
            return false;

        // 只接受数据帧（管理/控制帧不要解析 LLC）
        if (!(wh.IsData() || wh.IsQosData()))
            return false;

        // 只解析首片；后续分片（FragmentNumber>0）不含 LLC/SNAP
        if (wh.GetFragmentNumber() > 0)
            return false;

        p->RemoveHeader(wh);

        // 2) LLC/SNAP 必须存在且类型为 IPv4
        LlcSnapHeader llc;
        if (p->GetSize() < llc.GetSerializedSize())
            return false;
        if (!p->PeekHeader(llc))
            return false;
        if (llc.GetType() != 0x0800) // 0x0800 = IPv4
            return false;
        p->RemoveHeader(llc);

        // 3) IPv4 (+ 可选 UDP)
        if (!p->PeekHeader(ih))
            return false;
        p->RemoveHeader(ih);

        isUdp = (ih.GetProtocol() == 17); // UDP = 17
        if (!isUdp) return true; // 是 IPv4 但不是 UDP，也算“识别成功”

        return p->PeekHeader(uh); // 只有 UDP 再取 UDP 头
    }

    // ---- PHY monitor（能看到就说明“空口确实飞过”）----
    static void PhyMonRx(std::string ctx, Ptr<const Packet> pkt, uint16_t channelFreqMhz, WifiTxVector txv,
                         MpduInfo aMpdu,
                         SignalNoiseDbm sn, unsigned short staId) {
        auto &log_file = Log();
        Ipv4Header ih;
        UdpHeader uh;
        bool isUdp = false;
        if (PeekIpv4Udp(pkt, ih, uh, isUdp) && isUdp) {
            log_file << "[PHY-RXOK] node=" << CtxNode(ctx) << " dev=" << CtxDev(ctx)
                    << " " << ih.GetSource() << " -> " << ih.GetDestination()
                    << " sport=" << uh.GetSourcePort() << " dport=" << uh.GetDestinationPort()
                    << " mode=" << txv.GetMode(staId).GetUniqueName() << " staId=" << staId
                    << " sig=" << sn.signal << "dBm noise=" << sn.noise << "dBm\n";
        }
    }

    static void PhyMonTx(std::string ctx, Ptr<const Packet> pkt, uint16_t channelFreqMhz, WifiTxVector txv,
                         MpduInfo aMpdu,
                         uint16_t staId) {
        auto &log_file = Log();
        Ipv4Header ih;
        UdpHeader uh;
        bool isUdp = false;
        if (PeekIpv4Udp(pkt, ih, uh, isUdp) && isUdp) {
            log_file << "[PHY-TX]   node=" << CtxNode(ctx) << " dev=" << CtxDev(ctx)
                    << " " << ih.GetSource() << " -> " << ih.GetDestination()
                    << " sport=" << uh.GetSourcePort() << " dport=" << uh.GetDestinationPort()
                    << " mode=" << txv.GetMode(staId).GetUniqueName() << " staId=" << staId << "\n";
        }
    }

    // ---- MAC 侧（能看出是否队列/重传耗尽而丢）----
    static void MacTx(std::string ctx, Ptr<const Packet> pkt) {
        auto &log_file = Log();
        Ipv4Header ih;
        UdpHeader uh;
        bool isUdp = false;
        if (PeekIpv4Udp(pkt, ih, uh, isUdp) && isUdp) {
            log_file << "[MAC-TX]   node=" << CtxNode(ctx) << " dev=" << CtxDev(ctx)
                    << " " << ih.GetSource() << " -> " << ih.GetDestination()
                    << " sport=" << uh.GetSourcePort() << " dport=" << uh.GetDestinationPort() << "\n";
        }
    }

    static void MacTxDrop(std::string ctx, Ptr<const Packet> pkt) {
        auto &log_file = Log();
        Ipv4Header ih;
        UdpHeader uh;
        bool isUdp = false;
        if (PeekIpv4Udp(pkt, ih, uh, isUdp) && isUdp) {
            log_file << "[MAC-DROP] node=" << CtxNode(ctx) << " dev=" << CtxDev(ctx)
                    << " " << ih.GetSource() << " -> " << ih.GetDestination()
                    << " sport=" << uh.GetSourcePort() << " dport=" << uh.GetDestinationPort() << "\n";
        }
    }

    // ---- 一键绑定：指定节点/设备（骨干口）----
    static void AddWifiL2Traces(uint32_t nodeId, uint32_t devIndex) {
        auto &log_file = Log();
        std::ostringstream base;
        base << "/NodeList/" << nodeId << "/DeviceList/" << devIndex << "/$ns3::WifiNetDevice/";
        Config::Connect(base.str() + "Phy/MonitorSnifferRx", MakeCallback(&PhyMonRx));
        Config::Connect(base.str() + "Phy/MonitorSnifferTx", MakeCallback(&PhyMonTx));
        Config::Connect(base.str() + "Mac/MacTx", MakeCallback(&MacTx));
        Config::Connect(base.str() + "Mac/MacTxDrop", MakeCallback(&MacTxDrop));
        log_file << "AddWifiL2Traces on node " << nodeId << " dev " << devIndex << "\n";
    }
} // namespace duavsim
